package me.zhengjie.modules.fund.service;

import me.zhengjie.modules.fund.service.dto.ReqSalaryCalculate;
import me.zhengjie.modules.fund.service.dto.ReqSalaryCalculate.SocialSecurityConfig;
import me.zhengjie.modules.fund.service.dto.RespAnnualSalaryCalculate;
import me.zhengjie.modules.fund.service.dto.RespAnnualSalaryCalculate.MonthSalaryDetail;
import me.zhengjie.modules.fund.service.dto.RespAnnualSalaryCalculate.PersonalTax;
import me.zhengjie.modules.fund.service.dto.RespAnnualSalaryCalculate.SocialSecurity;
import me.zhengjie.modules.fund.service.dto.RespAnnualSalaryCalculate.SocialSecurityItem;
import me.zhengjie.modules.fund.service.dto.SocialSecurityType;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 个人薪资计算器
 */
@Component
public class PersonalSalaryCalculator {

    /**
     * 默认起征点
     */
    private static final int TAX_THRESHOLD = 5000;

    private double add(double x, double y) {
        return BigDecimal.valueOf(x).add(BigDecimal.valueOf(y)).doubleValue();
    }

    private double subtract(double x, double y) {
        return BigDecimal.valueOf(x).subtract(BigDecimal.valueOf(y)).doubleValue();
    }

    private double multiply(double x, double y) {
        return BigDecimal.valueOf(x).multiply(BigDecimal.valueOf(y)).doubleValue();
    }

    /**
     * 计算年薪
     *
     * @param params 计算参数
     * @return
     */
    public RespAnnualSalaryCalculate CalculateAnnualSalary(ReqSalaryCalculate params) {
        RespAnnualSalaryCalculate result = new RespAnnualSalaryCalculate();
        List<MonthSalaryDetail> monthSalaryDetails = new ArrayList<>();
        for (int i = 1; i <= 12; i++) {
            ReqSalaryCalculate monthParams = new ReqSalaryCalculate();
            monthParams.setMonth(i);
            monthParams.setSalaryBeforeTax(params.getSalaryBeforeTax());
            monthParams.setSpecialAddonAmount(params.getSpecialAddonAmount());

            MonthSalaryDetail monthSalaryDetail = CalculateMonthSalary(monthParams);
            result.setTotalSalaryBeforeTax(add(result.getTotalSalaryBeforeTax(), monthSalaryDetail.getSalaryBeforeTax()));
            result.setTotalPersonalTax(add(result.getTotalPersonalTax(), monthSalaryDetail.getPersonalTax().getTaxAmount()));
            result.setTotalSocialSecurity(add(result.getTotalSocialSecurity(), monthSalaryDetail.getSocialSecurity().getTotalAmountOfPersonal()));
            result.setTotalSalaryAfterTax(add(result.getTotalSalaryAfterTax(), monthSalaryDetail.getSalaryAfterTax()));
            monthSalaryDetails.add(monthSalaryDetail);

        }
        result.setMonthDetails(monthSalaryDetails);
        return result;
    }

    /**
     * 计算月薪
     *
     * @param params 计算参数
     * @return 月薪资计算结果
     */
    public MonthSalaryDetail CalculateMonthSalary(ReqSalaryCalculate params) {

        MonthSalaryDetail.MonthSalaryDetailBuilder resultBuilder = MonthSalaryDetail.builder()
                .month(params.getMonth())
                .salaryBeforeTax(params.getSalaryBeforeTax());

        // 计算社保
        SocialSecurity socialSecurity = calculateSocialSecurity(params);
        resultBuilder.socialSecurity(socialSecurity);

        // 个人所得税
        PersonalTax personalTax = calculatePersonalTax2020(params);
        resultBuilder.personalTax(personalTax);

        // 税后工资 = 税前工资 - 交纳社保 - 个人所得税
        MonthSalaryDetail result = resultBuilder.build();
        result.setSalaryAfterTax(subtract(
                subtract(result.getSalaryBeforeTax(), socialSecurity.getTotalAmountOfPersonal()),
                personalTax.getTaxAmount())
        );
        return result;
    }

    /**
     * 计算社保
     *
     * @param params
     * @return
     */
    private SocialSecurity calculateSocialSecurity(ReqSalaryCalculate params) {
        SocialSecurity result = new SocialSecurity();

        // 解析参数
        resolveDeductionConfigs(params);

        List<SocialSecurityItem> items = new ArrayList<>();
        BigDecimal totalAmountOfPersonal = BigDecimal.valueOf(0);
        BigDecimal totalAmountOfCompany = BigDecimal.valueOf(0);
        for (SocialSecurityConfig config : params.getSocialSecurityConfigs()) {
            SocialSecurityItem socialSecurityItem = SocialSecurityItem.builder()
                    .type(config.getType())
                    .baseAmount(config.getBaseAmount())
                    .rateOfCompany(config.getRateOfCompany())
                    .rateOfPersonal(config.getRateOfPersonal())
                    .fixAmountOfPersonal(config.getFixAmountOfPersonal())
                    .build();
            // 如果工资小于基数，使用工资值
            if (socialSecurityItem.getBaseAmount() > params.getSalaryBeforeTax()) {
                socialSecurityItem.setBaseAmount(params.getSalaryBeforeTax());
            }

            // 个人交纳金额
            double amountOfPersonal = add(multiply(socialSecurityItem.getBaseAmount(), socialSecurityItem.getRateOfPersonal()), socialSecurityItem.getFixAmountOfPersonal());
            if (amountOfPersonal > config.getLimitAmountOfPersonal()) {
                amountOfPersonal = config.getLimitAmountOfPersonal();
            }
            socialSecurityItem.setAmountOfPersonal(amountOfPersonal);
            totalAmountOfPersonal = totalAmountOfPersonal.add(BigDecimal.valueOf(amountOfPersonal));

            // 企业交纳金额
            double amountOfCompany = BigDecimal.valueOf(socialSecurityItem.getBaseAmount()).multiply(BigDecimal.valueOf(socialSecurityItem.getRateOfCompany())).doubleValue();
            if (amountOfCompany > config.getLimitAmountOfCompany()) {
                amountOfCompany = config.getLimitAmountOfCompany();
            }
            socialSecurityItem.setAmountOfCompany(amountOfCompany);
            totalAmountOfCompany = totalAmountOfCompany.add(BigDecimal.valueOf(amountOfCompany));

            items.add(socialSecurityItem);
        }
        result.setTotalAmountOfCompany(totalAmountOfCompany.doubleValue());
        result.setTotalAmountOfPersonal(totalAmountOfPersonal.doubleValue());
        result.setItems(items);
        return result;
    }

    private void resolveDeductionConfigs(ReqSalaryCalculate params) {
        List<SocialSecurityConfig> defaultConfig = params.getDefaultConfig();

        for (SocialSecurityConfig config : defaultConfig) {
            SocialSecurityConfig paramsConfig = SocialSecurityConfig.builder().build();
            Optional<SocialSecurityConfig> paramsConfigOptional = params.getSocialSecurityConfigs().stream().filter(c -> c.getType().equals(config.getType())).findFirst();
            if (paramsConfigOptional.isPresent()) {
                paramsConfig = paramsConfigOptional.get();
            } else {
                if (config.getType().equals(SocialSecurityType.Shiye)) {
                    if (params.getMonth() >= 5 && config.getStartDate() == null) {
                        continue;
                    }
                }
                BeanUtils.copyProperties(config, paramsConfig);
                params.getSocialSecurityConfigs().add(paramsConfig);
            }
            switch (config.getType()) {
                case Gjj:
                    if (Objects.nonNull(params.getGjjBaseAmount()) && params.getGjjBaseAmount() > 0) {
                        paramsConfig.setBaseAmount(params.getGjjBaseAmount());
                    }
                    break;
                case Yanglao:
                case Shiye:
                case Gongshang:
                    if (Objects.nonNull(params.getSocialSecurityBase()) && params.getSocialSecurityBase() > 0) {
                        paramsConfig.setBaseAmount(params.getSocialSecurityBase());
                    }
                    break;
                case Yiliao:
                case Shengyu:
                    if (Objects.nonNull(params.getYiliaoBaseAmount()) && params.getYiliaoBaseAmount() > 0) {
                        paramsConfig.setBaseAmount(params.getYiliaoBaseAmount());
                    }
                    break;
            }
        }
    }

    /**
     * 计算个税
     * 本月累计应扣预缴纳税所得额= 累计收入 - 累计免税收入 - 累计减除费用 - 累计专项扣除 - 累计专项附加扣除 - 累计依法确定的其他扣除
     * 本月应扣缴税额 = （ 本月累计应扣预缴纳税所得额 * 预扣税率 - 速算扣除数）-累计减免税额 - 累计已预扣预缴税额
     *
     * @param params
     * @return
     */
    private PersonalTax calculatePersonalTax2020(ReqSalaryCalculate params) {
        double taxThreshold = TAX_THRESHOLD;
        if (Objects.nonNull(params.getTaxThreshold()) && params.getTaxThreshold() > 0) {
            taxThreshold = params.getTaxThreshold();
        }

        PersonalTax personalTax = PersonalTax.builder()
                .month(params.getMonth())
                .salaryBeforeTax(params.getSalaryBeforeTax())
                .totalSalaryBeforeTaxOfYear(BigDecimal.valueOf(params.getSalaryBeforeTax()).multiply(BigDecimal.valueOf(params.getMonth())).doubleValue())
                .totalSpecialAddonDeductionAmountOfYear(BigDecimal.valueOf(params.getSpecialAddonAmount()).multiply(BigDecimal.valueOf(params.getMonth())).doubleValue())
                .totalDeductionAmountOfYear(BigDecimal.valueOf(taxThreshold).multiply(BigDecimal.valueOf(params.getMonth())).doubleValue())
                .build();
        // 计算社保, 累计社保公积金
        for (int i = 1; i <= params.getMonth(); i++) {
            ReqSalaryCalculate socialSecurityParams = new ReqSalaryCalculate();
            socialSecurityParams.setMonth(i);
            socialSecurityParams.setSalaryBeforeTax(params.getSalaryBeforeTax());
            socialSecurityParams.setSpecialAddonAmount(params.getSpecialAddonAmount());
            SocialSecurity socialSecurity = calculateSocialSecurity(socialSecurityParams);
            personalTax.setTotalSocialDeductionAmountOfYear(add(personalTax.getTotalSocialDeductionAmountOfYear(), socialSecurity.getTotalAmountOfPersonal()));
        }

        // 累计应税收入 = 累计收入 - 累计减免 - 累计专项附加扣除 - 累计社保公积金
        double shouldTaxSalary = BigDecimal.valueOf(personalTax.getTotalSalaryBeforeTaxOfYear())
                .subtract(BigDecimal.valueOf(personalTax.getTotalDeductionAmountOfYear()))
                .subtract(BigDecimal.valueOf(personalTax.getTotalSpecialAddonDeductionAmountOfYear()))
                .subtract(BigDecimal.valueOf(personalTax.getTotalSocialDeductionAmountOfYear()))
                .doubleValue();
        personalTax.setShouldTaxSalary(shouldTaxSalary);
        if (shouldTaxSalary < 0) {
            personalTax.setShouldTaxSalary(0d);
        }

        // 税率表
        List<ReqSalaryCalculate.TaxRateTableItem> tagRateTables = params.getDefaultTaxRateTables();
        for (ReqSalaryCalculate.TaxRateTableItem rateTableItem : tagRateTables) {
            if (personalTax.getShouldTaxSalary() >= rateTableItem.getStart() && personalTax.getShouldTaxSalary() <= rateTableItem.getEnd()) {
                double shouldTaxAmount = BigDecimal.valueOf(personalTax.getShouldTaxSalary()).multiply(BigDecimal.valueOf(rateTableItem.getRate()))
                        .subtract(BigDecimal.valueOf(rateTableItem.getQuickDeductionAmount())).doubleValue();
                personalTax.setTotalShouldTaxAmountOfYear(shouldTaxAmount);
                personalTax.setTaxRate(rateTableItem.getRate());
                break;
            }
        }

        // 累计应纳税金额 = 之前月份纳税金额之和
        if (params.getMonth() == 1) {
            personalTax.setTotalHasTaxAmountOfYear(0d);
        } else {
            // 本月已交税额 = 上月累计应纳税额
            ReqSalaryCalculate lastedMonthParams = new ReqSalaryCalculate();
            lastedMonthParams.setMonth(params.getMonth() - 1);
            lastedMonthParams.setSalaryBeforeTax(params.getSalaryBeforeTax());
            lastedMonthParams.setSpecialAddonAmount(params.getSpecialAddonAmount());

            PersonalTax lastedPersonalTax = calculatePersonalTax2020(lastedMonthParams);
            personalTax.setTotalHasTaxAmountOfYear(lastedPersonalTax.getTotalShouldTaxAmountOfYear());
        }
        personalTax.setTaxAmount(BigDecimal.valueOf(personalTax.getTotalShouldTaxAmountOfYear()).subtract(BigDecimal.valueOf(personalTax.getTotalHasTaxAmountOfYear())).doubleValue());

        return personalTax;
    }
}
